package eu.boss.activities;

import java.util.ArrayList;
import java.util.List;
import java.util.Vector;

import org.apache.http.NameValuePair;
import org.apache.http.message.BasicNameValuePair;
import org.codehaus.jackson.map.ObjectMapper;

import Util.Util;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.os.AsyncTask.Status;
import android.os.Bundle;
import android.os.IBinder;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.Fragment;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager.OnPageChangeListener;
import android.util.Log;
import android.widget.Toast;

import com.actionbarsherlock.app.SherlockDialogFragment;
import com.actionbarsherlock.app.SherlockFragmentActivity;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuItem;
import com.actionbarsherlock.view.SubMenu;
import com.actionbarsherlock.view.Window;
import com.viewpagerindicator.LinePageIndicator;

import eu.boss.R;
import eu.boss.controller.ShadowStatsController;
import eu.boss.exceptions.LoginException;
import eu.boss.fragments.GameFragment;
import eu.boss.fragments.MyPagerAdapter;
import eu.boss.fragments.StatsFragment;
import eu.boss.fragments.TeamStatsFragment;
import eu.boss.http.Http;
import eu.boss.model.Player;
import eu.boss.model.StatsMessage;
import eu.boss.model.TeamType;

/**
 * This class links all fragments and makes
 * 
 * @author Arnaud
 * 
 */
public class FragmentsSliderActivity extends SherlockFragmentActivity {

	private static int NEW_PLAYER_ID = 131073; // I don't know why. Maybe
												// because the activity is
												// thrown from the fragment
	private static int UPDATE_STATS_ID = 3;
	private PagerAdapter mPagerAdapter;
	private ViewPager mPager;
	private ShadowStatsController mController;
	private ObjectMapper mMapper = new ObjectMapper();
	private PlayersPutTask mPutTask;
	private RefreshTask mRefreshTask;
	private LinePageIndicator mIndicator;
	private static int dialogMsgHelpToDisplay = 1;
	SharedPreferences sharedPreferences;

	List<Fragment> mFragments;

	private ServiceConnection mControllerConnection = new ServiceConnection() {
		// Called when the connection with the service is established
		public void onServiceConnected(ComponentName className, IBinder service) {
			mController = ((ShadowStatsController.LocalBinder) service)
					.getService();
			initiateFragments();
		}

		// Called when the connection with the service disconnects unexpectedly
		public void onServiceDisconnected(ComponentName className) {
			mController = null;
		}
	};

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
		super.onCreate(savedInstanceState);
		super.setContentView(R.layout.viewpager);
	}

	/**
	 * Change menu with the paging
	 */
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		if (mPager.getCurrentItem() == 0) {// teams stats page
			menu.add(getString(R.string.refresh))
					.setIcon(R.drawable.ic_refresh)
					.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
		}

		if (mPager.getCurrentItem() == 1) {// individual stats page
			menu.add(getString(R.string.refresh))
					.setIcon(R.drawable.ic_refresh)
					.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);

			SubMenu sub = menu.addSubMenu(getString(R.string.menu));
			sub.add(0, 0, 0, getString(R.string.init_scores));
			sub.add(0, 1, 0, getString(R.string.delete_players));
			sub.add(0, 2, 0, getString(R.string.disconnect));
			sub.add(0, 3, 0, getString(R.string.helpTitle));
			sub.add(0, 4, 0, getString(R.string.about));
			sub.getItem().setShowAsAction(
					MenuItem.SHOW_AS_ACTION_ALWAYS
							| MenuItem.SHOW_AS_ACTION_WITH_TEXT);
		} else if (mPager.getCurrentItem() == 2) {// new game page
			menu.add(getString(R.string.plus)).setIcon(R.drawable.ic_plus)
					.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
		}

		return true;
	}

	/**
	 * Callback method when an actionbar's option item is selected
	 */
	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		// This uses the imported MenuItem from ActionBarSherlock
		mIndicator.notifyDataSetChanged();
		if (item.getTitle().toString().compareTo(getString(R.string.refresh)) == 0)
			updatePlayerList();
		else if (item.getTitle().toString().compareTo(getString(R.string.plus)) == 0) {
			if (mController.getStatsMessage().getPlayers().size() == 0)
				toastMessage(getString(R.string.no_player_yet));

			else
				startActivityForResult(new Intent(FragmentsSliderActivity.this,
						UpdateStatsActivity.class), UPDATE_STATS_ID);
		}

		else if (item.getTitle().toString().compareTo(getTitle().toString()) == 0)
			startActivity(new Intent(FragmentsSliderActivity.this,
					AboutActivity.class));

		else if (item.getTitle().toString()
				.compareTo(getString(R.string.init_scores)) == 0) {
			if (mController.getStatsMessage().getPlayers().size() == 0)
				toastMessage(getString(R.string.no_player_yet));

			else {
				AlertDialog.Builder adb = new AlertDialog.Builder(this);
				adb.setTitle(getString(R.string.init_scores));
				adb.setMessage(getString(R.string.are_you_sure));
				adb.setPositiveButton("Ok",
						new DialogInterface.OnClickListener() {

							public void onClick(DialogInterface dialog,
									int which) {
								mController.getStatsMessage()
										.reinitPlayerList();
								mController.getStatsMessage().reinitTeamList();
								StatsFragment fragment = (StatsFragment) mFragments
										.get(1);
								fragment.updateList();
								updateServer();
							}
						});
				adb.setNegativeButton("Annuler", null);
				adb.show();
			}
		}

		else if (item.getTitle().toString()
				.compareTo(getString(R.string.delete_players)) == 0) {
			if (mController.getStatsMessage().getPlayers().size() == 0)
				toastMessage(getString(R.string.no_player_yet));

			else {
				AlertDialog.Builder adb = new AlertDialog.Builder(this);
				adb.setTitle(getString(R.string.delete_players));
				adb.setMessage(getString(R.string.are_you_sure));
				adb.setPositiveButton("Ok",
						new DialogInterface.OnClickListener() {

							public void onClick(DialogInterface dialog,
									int which) {
								mController.getStatsMessage().deletePlayers();
								StatsFragment fragment = (StatsFragment) mFragments
										.get(1);
								fragment.updateList();
								updateServer();
							}
						});

				adb.setNegativeButton("Annuler", null);
				adb.show();
			}

		} else if (item.getTitle().toString()
				.compareTo(getString(R.string.disconnect)) == 0) {
			AlertDialog.Builder adb = new AlertDialog.Builder(this);
			adb.setTitle(getString(R.string.disconnect));
			adb.setMessage(getString(R.string.are_you_sure));
			adb.setPositiveButton("Ok", new DialogInterface.OnClickListener() {

				public void onClick(DialogInterface dialog, int which) {
					SharedPreferences preferences = getSharedPreferences(
							"prefs", MODE_PRIVATE);
					preferences.edit().remove("hash").commit();
					startActivity(new Intent(FragmentsSliderActivity.this,
							LoginActivity.class));
					mController.setHash(null);
					if (mController.getStatsMessage() != null)
						mController.getStatsMessage().setPlayers(null);
					finish();
				}
			});
			adb.setNegativeButton("Annuler", null);
			adb.show();
		} else if (item.getTitle().toString()
				.compareTo(getString(R.string.about)) == 0) {
			startActivity(new Intent(FragmentsSliderActivity.this,
					AboutActivity.class));
		} else if (item.getTitle().toString()
				.compareTo(getString(R.string.helpTitle)) == 0) {
			dialogMsgHelpToDisplay = 1;
			showDialog();
		}
		return true;
	}

	@Override
	protected void onStart() {
		super.onStart();
		this.setTitle(getString(R.string.player_ranking));
		// new intent to bind service
		Intent intent = new Intent(this, ShadowStatsController.class);
		getApplicationContext().bindService(intent, mControllerConnection,
				Context.BIND_AUTO_CREATE);
	}

	/**
	 * Create the fragment List, set OnPageChangeListener on them to update the
	 * actionbar when new fragment is selected
	 */
	public void initiateFragments() {
		// Creation of Fragment list for paging
		mFragments = new Vector<Fragment>();

		// Adding Fragments into the list
		mFragments.add(Fragment.instantiate(this,
				TeamStatsFragment.class.getName()));
		mFragments
				.add(Fragment.instantiate(this, StatsFragment.class.getName()));
		mFragments
				.add(Fragment.instantiate(this, GameFragment.class.getName()));

		// Adapter's creation caring about displaying fragment list
		this.mPagerAdapter = new MyPagerAdapter(
				super.getSupportFragmentManager(), mFragments);

		// Set the pager with an adapter
		mPager = (ViewPager) super.findViewById(R.id.viewpager);
		// Affectation de l'adapter au ViewPager
		mPager.setAdapter(this.mPagerAdapter);

		// Bind the title indicator to the adapter
		mIndicator = (LinePageIndicator) findViewById(R.id.indicator);
		mIndicator.setViewPager(mPager);
		setSupportProgressBarIndeterminateVisibility(false);

		mIndicator.setOnPageChangeListener(new OnPageChangeListener() {

			public void onPageSelected(int position) {
				if (position == 0) {
					FragmentsSliderActivity.this
							.setTitle(getString(R.string.team_ranking));
					TeamStatsFragment fragment = (TeamStatsFragment) mFragments
							.get(position);
					fragment.displayResults();
					invalidateOptionsMenu();
				}
				if (position == 1) {
					FragmentsSliderActivity.this
							.setTitle(getString(R.string.player_ranking));
					StatsFragment fragment = (StatsFragment) mFragments
							.get(position);
					fragment.updateList();
					invalidateOptionsMenu();
				} else if (position == 2) {
					FragmentsSliderActivity.this
							.setTitle(getString(R.string.new_game));
					GameFragment fragment = (GameFragment) mFragments
							.get(position);
					fragment.updateList();
					invalidateOptionsMenu();
				}
			}

			public void onPageScrolled(int position, float positionOffset,
					int positionOffsetPixels) {
			}

			public void onPageScrollStateChanged(int state) {
			}
		});
		mPager.setCurrentItem(1);
		sharedPreferences = getSharedPreferences("prefs", MODE_PRIVATE);
		if (sharedPreferences.getBoolean("showHelp", true)) {
			showDialog();
			SharedPreferences.Editor editor = sharedPreferences.edit();
			editor.putBoolean("showHelp", false);
			editor.commit();
		}
	}

	/**
	 * Method refreshing playerList from the server. Error message displayed if
	 * no internet connection
	 */
	public void updatePlayerList() {
		if (Util.isOnline(this)) {
			if (mRefreshTask != null
					&& mRefreshTask.getStatus() != Status.FINISHED) {
				mRefreshTask.cancel(true);
			}
			mRefreshTask = new RefreshTask();
			mRefreshTask.execute();
		} else
			toastMessage(getString(R.string.connection_error));
	}

	/**
	 * Method launching server update. Error message displayed if no internet
	 * connection
	 */
	public void updateServer() {
		if (Util.isOnline(this)) {
			if (mPutTask != null && mPutTask.getStatus() != Status.FINISHED) {
				mPutTask.cancel(true);
			}
			mPutTask = new PlayersPutTask();
			mPutTask.execute();
		} else
			toastMessage(getString(R.string.connection_error));
	}

	/**
	 * onActivityResult
	 */
	public void onActivityResult(int requestCode, int resultCode, Intent data) {
		if ((requestCode == UPDATE_STATS_ID) && (resultCode == RESULT_OK)) {
			// a new game was added
			Bundle extras = data.getExtras();
			GameFragment fragment = (GameFragment) mFragments.get(2);
			// We get 3 extras from UpdateStatsActivity. status=0:won; status=1:
			// lost; team=0:shadow; team=1:hunter; team=2:neutral

			if (extras != null && extras.containsKey("gameStatus")) {
				int status = extras.getInt("gameStatus");
				int team = extras.getInt("team");
				int position;
				boolean sIncremented = false, hIncremented = false;
				for (position = 0; position < fragment.getCheckedState().length; position++) {
					if (fragment.getCheckedState()[position] == true) {
						switch (team) {
						case 0:
							if (status == 0) {
								mController.getStatsMessage().getPlayers()
										.get(position).incrementWS();
								if (!sIncremented) {
									mController.getStatsMessage()
											.getTeam(TeamType.SHADOW)
											.incrementWin();
									sIncremented = true;
								}
							} else {
								mController.getStatsMessage().getPlayers()
										.get(position).incrementLS();
								if (!sIncremented) {
									mController.getStatsMessage()
											.getTeam(TeamType.SHADOW)
											.incrementLost();
									sIncremented = true;
								}
							}
							break;

						case 1:
							if (status == 0) {
								mController.getStatsMessage().getPlayers()
										.get(position).incrementWH();
								if (!hIncremented) {
									mController.getStatsMessage()
											.getTeam(TeamType.HUNTER)
											.incrementWin();
									hIncremented = true;
								}
							} else {
								mController.getStatsMessage().getPlayers()
										.get(position).incrementLH();
								if (!hIncremented) {
									mController.getStatsMessage()
											.getTeam(TeamType.HUNTER)
											.incrementLost();
									hIncremented = true;
								}
							}
							break;

						case 2:
							if (status == 0) {
								mController.getStatsMessage().getPlayers()
										.get(position).incrementWN();
								mController.getStatsMessage()
										.getTeam(TeamType.NEUTRAL)
										.incrementWin();
							} else {
								mController.getStatsMessage().getPlayers()
										.get(position).incrementLN();
								mController.getStatsMessage()
										.getTeam(TeamType.NEUTRAL)
										.incrementLost();
							}
							break;
						default:
							break;
						}
					}
				}

				fragment.updateList();
				updateServer();
			}
		}

		else if ((requestCode == NEW_PLAYER_ID) && (resultCode == RESULT_OK)) {
			// a new player was created
			Bundle extras = data.getExtras();

			if (extras != null && extras.containsKey("player")) {
				String player = extras.getString("player");
				mapPlayer(player);
				StatsFragment fragment = (StatsFragment) mFragments.get(1);
				fragment.updateList();
			}
			updateServer();
		}
	}

	/**
	 * Deletes player
	 * 
	 * @param position
	 *            player to delete
	 */
	public void deletePlayer(int position) {
		StatsFragment fragment = (StatsFragment) mFragments.get(1);
		mController.getStatsMessage().getPlayers().remove(position);
		fragment.updateList();
		updateServer();
	}

	/**
	 * inits player
	 * 
	 * @param position
	 */
	public void initPlayer(int position) {
		StatsFragment fragment = (StatsFragment) mFragments.get(1);
		mController.getStatsMessage().getPlayers().get(position).reInit();
		fragment.updateList();
		updateServer();
	}

	/**
	 * Builds a player type with a string
	 * 
	 * @param strPlayer
	 */
	private void mapPlayer(String strPlayer) {
		Player player = null;
		try {
			player = mMapper.readValue(strPlayer, Player.class);
			mController.getStatsMessage().getPlayers().add(player);
		} catch (Exception e) {
			Log.e("mapPlayerError", e.getMessage());
		}
	}

	void showDialog() {
		DialogFragment newFragment = null;
		switch (dialogMsgHelpToDisplay) {
		case 1:
			newFragment = ShadowAlertDialogFragment.newInstance(R.string.help1);
			break;
		case 2:
			newFragment = ShadowAlertDialogFragment.newInstance(R.string.help2);
			break;
		case 3:
			newFragment = ShadowAlertDialogFragment.newInstance(R.string.help3);
			break;
		case 4:
			newFragment = ShadowAlertDialogFragment.newInstance(R.string.help4);
			break;
		default:
			break;
		}
		if (newFragment != null)
			newFragment.show(getSupportFragmentManager(), "dialog");
	}

	public void doPositiveClick() {
		// Do stuff here.
		if (dialogMsgHelpToDisplay < 5) {
			dialogMsgHelpToDisplay++;
			showDialog();
		}
		Log.i("FragmentAlertDialog", "Positive click!");
	}

	public void doNegativeClick() {
		// Do stuff here.
		Log.i("FragmentAlertDialog", "Negative click!");
	}

	public static class ShadowAlertDialogFragment extends
			SherlockDialogFragment {

		public static ShadowAlertDialogFragment newInstance(int message) {
			ShadowAlertDialogFragment frag = new ShadowAlertDialogFragment();
			Bundle args = new Bundle();
			args.putInt("message", message);
			frag.setArguments(args);
			return frag;
		}

		@Override
		public Dialog onCreateDialog(Bundle savedInstanceState) {
			int messageId = getArguments().getInt("message");
			int positiveBtnLabel;
			if (dialogMsgHelpToDisplay != 4)
				positiveBtnLabel = R.string.next;
			else
				positiveBtnLabel = R.string.ok;

			return new AlertDialog.Builder(getActivity())
					.setIcon(R.drawable.ic_help)
					.setTitle(R.string.helpTitle)
					.setMessage(messageId)
					.setPositiveButton(positiveBtnLabel,
							new DialogInterface.OnClickListener() {
								public void onClick(DialogInterface dialog,
										int whichButton) {
									((FragmentsSliderActivity) getActivity())
											.doPositiveClick();
								}
							})
					.setNegativeButton(R.string.cancel,
							new DialogInterface.OnClickListener() {
								public void onClick(DialogInterface dialog,
										int whichButton) {
									((FragmentsSliderActivity) getActivity())
											.doNegativeClick();
								}
							}).create();
		}
	}

	/**
	 * AsyncTask making HTTP Put request in a second thread. Updating server
	 * data
	 * 
	 * @author Arnaud
	 * 
	 */
	private class PlayersPutTask extends AsyncTask<Void, Void, String> {

		String error = null;

		@Override
		protected String doInBackground(Void... params) {

			String url = getString(R.string.web_app_url)
					+ getString(R.string.updatestats);
			String response = null;
			Http request = new Http();

			try {
				// Creation of a pair Key/Value to put on the server and get
				// response.
				String s = mMapper.writeValueAsString(mController
						.getStatsMessage());

				ArrayList<NameValuePair> pairs = new ArrayList<NameValuePair>();
				pairs.add(new BasicNameValuePair("statsMessage", s));
				response = request.executePutRequest(url, pairs,
						mController.getHash());

			} catch (Exception e) {
				error = e.toString();
			}
			return response;
		}

		@Override
		protected void onPostExecute(String result) {
			super.onPostExecute(result);
			if (error == null)
				if (mPager.getCurrentItem() == 1) {
					StatsFragment fragment = (StatsFragment) mFragments.get(1);
					fragment.updateList();
				} else if (mPager.getCurrentItem() == 2) {
					GameFragment fragment = (GameFragment) mFragments.get(2);
					fragment.updateList();
				}

				else
					displayErrorMessage(error);
		}
	};

	/**
	 * AsyncTask making HTTP Get request (second thread)
	 * 
	 * @author Arnaud
	 * 
	 */
	private class RefreshTask extends AsyncTask<Void, Void, StatsMessage> {

		String error = null;

		@Override
		protected void onPreExecute() {
			setSupportProgressBarIndeterminateVisibility(true);
		}

		@Override
		protected StatsMessage doInBackground(Void... params) {

			StatsMessage statsMsg = null;
			String response = null;
			ObjectMapper jacksonMapper = new ObjectMapper();
			/** PlayersListRequest */
			Http request = new Http();
			String url = getString(R.string.web_app_url)
					+ getString(R.string.login_url);
			try {
				// Creation of a pair Key/Value to put on the server and get
				// response.
				ArrayList<NameValuePair> pairs = new ArrayList<NameValuePair>();
				pairs.add(new BasicNameValuePair("hash", mController.getHash()));
				response = request.executePostRequest(url, pairs,
						mController.getHash());
				if (response.compareTo("LOGIN ERROR") == 0)
					throw new LoginException(FragmentsSliderActivity.this);

				statsMsg = jacksonMapper
						.readValue(response, StatsMessage.class);

			} catch (Exception e) {
				error = e.getMessage();
			}

			return statsMsg;
		}

		@Override
		protected void onPostExecute(StatsMessage result) {
			// updating playerList
			setSupportProgressBarIndeterminateVisibility(false);
			if (error == null) {
				mController.setStatsMessage(result);
				if (mPager.getCurrentItem() == 0) {
					TeamStatsFragment f1 = (TeamStatsFragment) mFragments
							.get(0);
					f1.displayResults();
				} else {
					StatsFragment f2 = (StatsFragment) mFragments.get(1);
					f2.updateList();
				}
			} else {
				displayErrorMessage(error);
			}
		}
	};

	/**
	 * Displaying error message in an AlertDialog
	 * 
	 * @param msg
	 *            message to display
	 */
	private void displayErrorMessage(String msg) {
		AlertDialog.Builder builder = new AlertDialog.Builder(this);
		builder.setTitle(getString(R.string.error));
		builder.setMessage(msg);
		builder.setPositiveButton(R.string.ok, null);
		builder.show();
	}

	/**
	 * Displaying message in a toast
	 * 
	 * @param msg
	 *            message to display
	 */
	private void toastMessage(String msg) {
		Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_LONG).show();

	}

	public ShadowStatsController getController() {
		return mController;
	}

	public void setController(ShadowStatsController mController) {
		this.mController = mController;
	}

}